// 16.1 定义模板
/**
 * 模板是C++中泛型编程的基础。一个模板就是一个创建类或函数的蓝图或者说公式。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;
#include <functional>
#include "../VisualStudio2012/15/Quote.h"
#include "../VisualStudio2012/12/TextQuery.h"

template <typename T>
int compare(const T &v1, const T &v2)
{
    if(v1 < v2) return -1;
    if(v2 < v1) return 1;
    return 0;
}

// 正确：返回类型和参数类型相同
template <typename T> T Foo(T* p)
{
    T tmp = *p; // tmp的类型是指针指向的类型
    // ...
    return tmp;
}

template <unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M]) // const char (&p1)[N] 表示“一个长度为 N 的 const char 类型的数组的引用”
{
    return strcmp(p1, p2);
}

template <typename T> int compare1(const T &v1, const T &v2)
{
    if(less<T>()(v1,v2)) return -1;
    if(less<T>()(v2,v1)) return 1;
    return 0;
}

template <typename T> class Blob {
public:
    typedef T value_type;
    // typename 关键字在这里的作用是告诉编译器我们正在使用的是一个依赖于模板参数 T 的类型。由于 std::vector<T> 是一个模板类，它的成员类型可能会包含一些依赖于模板参数的类型，
    // 如 size_type 和 difference_type 等。在这种情况下，我们需要使用 typename 来明确地告诉编译器我们在引用这些嵌套类型时是在指代一个类型。
    // 换句话说，如果省略了 typename，编译器可能无法确定 std::vector<T>::size_type 是否是一个类型名称，还是只是一个普通的值。
    // 因此，typename 在这里用于消除这种歧义，并确保编译器正确解析为一个类型名称。
    typedef typename vector<T>::size_type size_type; 
    // 构造函数
    Blob();
    // 在C++中，std::initializer_list<T> 是一个标准库类型，它代表了一个不可修改的元素类型为 T 的列表。这个类型的对象通常用于初始化其他容器或聚合类型（如数组）。
    // 例如：Blob<int> myBlob = {1, 2, 3, 4, 5}; 在这个例子中，{1, 2, 3, 4, 5} 就是一个 initializer list
    Blob(std::initializer_list<T> il);
    // Blob中的元素数目
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    // 添加和删除元素
    void push_back(const T &t) { data->push_back(t); }
    // 移动版本 参见13.6.3节
    void push_back(T &&t) { data->push_back(std::move(t)); }
    void pop_back();
    // 元素访问
    T& back();
    T& operator[](size_type i); // 14.5节中定义
private:
    shared_ptr<vector<T>> data;
    // 若data[i]无效，则抛出msg
    void check(size_type i, const string &msg) const;
};

template<typename T>
void Blob<T>::check(size_type i, const string &msg) const
{
    if(i >= data->size())
        throw out_of_range(msg);
}
template<typename T>
T& Blob<T>::back()
{
    check(0, "back on empty Blob");
    return data->back();
}
template<typename T>
T& Blob<T>::operator[](size_type i)
{
    // 如果i太大，check会抛出异常，阻止访问一个不存在的元素
    check(i, "subscript out of range");
    return data->operator[](i);
}
template<typename T>
void Blob<T>::pop_back()
{
    check(0, "pop_back on empty Blob");
    data->pop_back();
}

template<typename T>
Blob<T>::Blob() : data(std::make_shared<std::vector<T>>()) {}
template<typename T>
Blob<T>::Blob(std::initializer_list<T> il) : data(std::make_shared<std::vector<T>>(il)) {}

// 若试图访问一个不存在的元素，BlobPtr抛出一个异常
template<typename T> class BlobPtr {
public:
    BlobPtr() : curr(0) {}
    BlobPtr(Blob<T> &a, size_t sz = 0) : wptr(a.data), curr(sz) {}
    T& operator*() const 
    { 
        auto p = check(curr, "dereference past end");
        return (*p)[curr]; // p为本对象指向的vector
    }
    // 递增和递减
    BlobPtr& operator++(); // 前置运算符
    BlobPtr& operator--();
    BlobPtr operator++(int); // 后置运算符
    BlobPtr operator--(int);
    // BlobPtr的前置递增和递减成员返回BlobPtr&，而不是BlobPtr<T>&。当我们处于一个类模板的作用域中时，编译器处理模板自身引用时就好像我们已经提供了与模板参数匹配的实参一样。
private:
    // 若检查成功，check返回一个指向vector的指针
    shared_ptr<vector<T>> check(size_t, const string &) const;
    // 保存一个weak_ptr，表示底层vector可能被销毁
    std::weak_ptr<vector<T>> wptr;
    std::size_t curr;
};

// 后置：递增/递减对象但返回原值
template<typename T>
BlobPtr<T> BlobPtr<T>::operator++(int)
{
    // 此处无需检查，调用前置递增时会进行检查
    BlobPtr ret = *this; // 保存当前值
    ++*this;
    return ret;
    // 卧槽真的牛逼，原来后置递增运算是这样实现的呀！！卧槽牛逼！！
    // 在一个类模板的作用域内，我们可以直接使用模板名而不必指定模板实参。
}

template<typename T>
typename T::value_type top(const T& c)
{
    if(!c.empty())
        return c.back();
    else
        return typename T::value_type();
}

int main()
{
    // 16.1.1 函数模板
    // 一个函数模板就是一个公式，可用来生成针对特定类型的函数版本。compare的模板版本可能像下面这样：
    // 模板定义以关键字template开始，后跟一个模板参数列表（template parameter list），这是一个逗号分隔的一个或多个模板参数（template parameter）的列表，用小于号（<）和大于号（>）包围起来。
    // 函数参数列表定义了若干特定类型的局部变量，但并未指出如何初始化它们。在运行时，调用者提供实参来初始化形参。
    // 类似的，模板参数表示在类或函数定义中用到的类型或值。当使用模板时，我们（隐式地或显式地）指定模板实参（template argument），将其绑定到模板参数上。

    // 实例化函数模板
    // 当我们调用一个函数模板时，编译器（通常）用函数实参来为我们推断模板实参。即，当我们调用compare时，编译器使用实参的类型来确定绑定到模板参数T的类型。
    cout << compare(1,0) << endl;
    // 编译器用推断出的模板参数来为我们实例化（instantiate）一个特定版本的函数。编译器生成的不同版本的函数通常被称为模板的实例（instantiation）。

    // 模板类型参数
    // 我们的compare函数有一个模板类型参数（type parameter）。一般来说，我们可以将类型参数看作类型说明符，就像内置类型或类类型说明符一样使用。
    // 特别是，类型参数可以用来指定返回类型或函数的参数类型，以及在函数体内用于变量声明或类型转换：
    // 类型参数前必须使用关键字class或typename：
    //   错误：U之前必须加上class或typename
    //   template<typename T, U> T calc(const T&, const U&);
    // 在模板参数列表中，这两个关键字的含义相同，可以互换使用。一个模板参数列表中可以同时使用这两个关键字：
    //   正确：在模板参数列表中，typename和class没有什么不同
    //   template<typename T, class U> T calc(const T&, const U&);
    // 看起来用关键字typename来指定模板类型参数比用class更为直观。typename是在模板已经广泛使用之后才引入C++语言的，某些程序员仍然只用class。

    // 非类型模板参数
    // 除了定义类型参数，还可以在模板中定义非类型参数（nontype parameter）。一个非类型参数表示一个值而非一个类型。我们通过一个特定的类型名而非关键字class或typename来指定非类型参数。
    // 当一个模板被实例化时，非类型参数被一个用户提供的或编译器推断出的值所代替。这些值必须是常量表达式（参见2.4.4节，第58页），从而允许编译器在编译时实例化模板。
    // 例如，我们可以编写一个compare版本处理字符串字面常量。这种字面常量是const char的数组。由于不能拷贝一个数组，所以我们将自己的参数定义为数组的引用（参见6.2.4节，第195页）。
    //   由于我们希望能比较不同长度的字符串字面常量，因此为模板定义了两个非类型的参数。第一个模板参数表示第一个数组的长度，第二个参数表示第二个数组的长度：
    // 当我们调用这个版本的compare时：
    //   compare("hi", "mom");
    // 编译器会使用字面常量的大小来代替N和M，从而实例化模板。记住，编译器会在一个字符串字面常量的末尾插入一个空字符作为终结符（参见2.1.3节，第36页），因此编译器会实例化出如下版本：
    //   int compare(const char (&p1)[3], const char (&p2)[4])
    // 一个非类型参数可以是一个整型，或者是一个指向对象或函数类型的指针或（左值）引用。绑定到非类型整型参数的实参必须是一个常量表达式。
    //   绑定到指针或引用非类型参数的实参必须具有静态的生存期（参见第12章，第400页）。
    // 在模板定义内，模板非类型参数是一个常量值。在需要常量表达式的地方，可以使用非类型参数，例如，指定数组大小。

    // inline和constexpr的函数模板
    // 函数模板可以声明为inline或constexpr的，如同非模板函数一样。inline或constexpr说明符放在模板参数列表之后，返回类型之前：
    //   正确：inline说明符跟在模板参数列表之后
    //   template<typename T> inline T min(cosnt T&, const T&);
    //   错误：inline说明符的位置不正确
    //   inline template<typename T> T min(cosnt T&, const T&); 

    // 编写类型无关的代码
    // 我们最初的compare函数虽然简单，但它说明了编写泛型代码的两个重要原则：· 模板中的函数参数是const的引用。· 函数体中的条件判断仅使用<比较运算。
    // 通过将函数参数设定为const的引用，我们保证了函数可以用于不能拷贝的类型。大多数类型，包括内置类型和我们已经用过的标准库类型（除unique_ptr和IO类型之外），都是允许拷贝的。
    //   但是，不允许拷贝的类类型也是存在的。
    // 你可能认为既使用<运算符又使用>运算符来进行比较操作会更为自然：[插图]
    //   // 期望的比较操作
    //   if(v1 < v2) return -1;
    //   if(v1 > v2) return 1;
    //   return 0;
    //   但是，如果编写代码时只使用<运算符，我们就降低了compare函数对要处理的类型的要求。这些类型必须支持<，但不必同时支持>。
    // 实际上，如果我们真的关心类型无关和可移植性，可能需要用less（参见14.8.2节，第510页）来定义我们的函数：

    // 模板编译
    // 当我们使用（而不是定义）模板时，编译器才生成代码，这一特性影响了我们如何组织代码以及错误何时被检测到。
    // 通常，当我们调用一个函数时，编译器只需要掌握函数的声明。类似的，当我们使用一个类类型的对象时，类定义必须是可用的，但成员函数的定义不必已经出现。
    //   因此，我们将类定义和函数声明放在头文件中，而普通函数和类的成员函数的定义放在源文件中。
    // 模板则不同：为了生成一个实例化版本，编译器需要掌握函数模板或类模板成员函数的定义。因此，与非模板代码不同，模板的头文件通常既包括声明也包括定义。
    // 函数模板和类模板成员函数的定义通常放在头文件中。

    // 关键概念：模板和头文件
    // 模板的设计者应该提供一个头文件，包含模板定义以及在类模板或成员定义中用到的所有名字的声明。模板的用户必须包含模板的头文件，以及用来实例化模板的任何类型的头文件。

    // 大多数编译错误在实例化期间报告
    // 模板直到实例化时才会生成代码，这一特性影响了我们何时才会获知模板内代码的编译错误。通常，编译器会在三个阶段报告错误。
    //   第一个阶段是编译模板本身时。第二个阶段是编译器遇到模板使用时。第三个阶段是模板实例化时。
    // 保证传递给模板的实参支持模板所要求的操作，以及这些操作在模板中能正确工作，是调用者的责任。

    // 16.1.2 类模板
    // 类模板（class template）是用来生成类的蓝图的。与函数模板的不同之处是，编译器不能为类模板推断模板参数类型。

    // 定义类模板
    // 类似函数模板，类模板以关键字template开始，后跟模板参数列表。在类模板（及其成员）的定义中，我们将模板参数当作替身，代替使用模板时用户需要提供的类型或值：

    // 实例化类模板
    // 一个类模板的每个实例都形成一个独立的类。类型Blob<string>与任何其他Blob类型都没有关联，也不会对任何其他Blob类型的成员有特殊访问权限。

    // 在模板作用域中引用模板类型
    // 可能令人迷惑的是，一个类模板中的代码如果使用了另外一个模板，通常不将一个实际类型（或值）的名字用作其模板实参。相反的，我们通常将模板自己的参数当作被使用模板的实参。
    // 例如，我们的data成员使用了两个模板，vector和shared_ptr。我们知道，无论何时使用模板都必须提供模板实参。在本例中，我们提供的模板实参就是Blob的模板参数。因此，data的定义如下：
    //   std::shared_ptr<std::vector<T>> data;
    // 它使用了Blob的类型参数来声明data是一个shared_ptr的实例，此shared_ptr指向一个保存类型为T的对象的vector实例。

    // 类模板的成员函数
    // 与其他任何类相同，我们既可以在类模板内部，也可以在类模板外部为其定义成员函数，且定义在类模板内的成员函数被隐式声明为内联函数。
    // 类模板的每个实例都有其自己版本的成员函数。因此，类模板的成员函数具有和模板相同的模板参数。因而，定义在类模板之外的成员函数就必须以关键字template开始，后接类模板参数列表。
    // 例如：template<typename T> ret-val Blob<T>::member-func(const T& val) {... } 

    // check和元素访问成员

    // Blob构造函数

    // 类模板成员函数的实例化
    // 默认情况下，一个类模板的成员函数只有当程序用到它时才进行实例化。例如，下面代码
    // 实例化Blob<int>和接受initializer_list的构造函数
    Blob<int> squares = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    // 实例化Blob<int>::size() const
    for (size_t i = 0; i!= squares.size(); ++i)
        squares[i] = i*i; // 实例化Blob<int>::operator[](size_t)
    // 如果一个成员函数没有被使用，则它不会被实例化。成员函数只有在被用到时才进行实例化，这一特性使得即使某种类型不能完全符合模板操作的要求（参见9.2节，第294页），我们仍然能用该类型实例化类。
    // 默认情况下，对于一个实例化了的类模板，其成员只有在使用时才被实例化。

    // 在类代码内简化模板类名的使用
    // 当我们使用一个类模板类型时必须提供模板实参，但这一规则有一个例外。在类模板自己的作用域中，我们可以直接使用模板名而不提供实参：
    // BlobPtr的前置递增和递减成员返回BlobPtr&，而不是BlobPtr<T>&。当我们处于一个类模板的作用域中时，编译器处理模板自身引用时就好像我们已经提供了与模板参数匹配的实参一样。

    // 在类模板外使用类模板名
    // 当我们在类模板外定义其成员时，必须记住，我们并不在类的作用域中，直到遇到类名才表示进入类的作用域（参见7.4节，第253页）：
    // 在一个类模板的作用域内，我们可以直接使用模板名而不必指定模板实参。

    // 类模板和友元
    // 当一个类包含一个友元声明（参见7.2.1节，第241页）时，类与友元各自是否是模板是相互无关的。如果一个类模板包含一个非模板友元，则友元被授权可以访问所有模板实例。
    // 如果友元自身是模板，类可以授权给所有友元模板实例，也可以只授权给特定实例。

    // 一对一友好关系
    // 为了引用（类或函数）模板的一个特定实例，我们必须首先声明模板自身。一个模板声明包括模板参数列表：
    /**
     * // 前置声明，在Blob中声明友元所需要的
     * template<typename> class BlobPtr;
     * template<typename> class Blob; // 运算符==中的参数所需要的
     * template<typename T> bool operator==(const Blob<T>&, const Blob<T>&);
     * 
     * template<typename T> class Blob {
     *     // 每个Blob实例将访问权限授予用相同类型实例化的BlobPtr和==相等运算符
     *     friend class BlobPtr<T>;
     *     friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
     *     // 其他成员定义与12.1.1节相同
     * };
    */

    // 通用和特定的模板友好关系
    // 一个类也可以将另一个模板的每个实例都声明为自己的友元，或者限定特定的实例为友元：
    /**
     * // 前置声明，在将一个模板的特定实例声明为友元时要用到
     * template<typename T> class Pal;
     * class C {
     *     friend class Pal<C>; // 用类C实例化的Pal是C的一个友元
     *     // Pal2的所有实例都是C的友元，这种情况无需前置声明
     *     template<typename T> friend class Pal2;
     * };
     * template<typename T> class C2 { // C2本身是一个类模板
     *     // C2的每个实例将相同实例化的Pal声明为友元
     *     friend class Pal<T>; // Pal的模板声明必须在作用域内
     *     // Pal2的所有实例都是C2所有实例的友元，不需要前置声明
     *     template<typename X> friend class Pal2;
     *     // Pal3是非模板类，它是C2所有实例的友元
     *     friend class Pal3;
     * };
    */

    // 令模板自己的类型参数成为友元
    // 在新标准中，我们可以将模板类型参数声明为友元：
    // template<typename T> class Bar {
    //     friend T; // 将访问权限授予用来实例化Bar的类型
    // };
    // 值得注意的是，虽然友元通常来说应该是一个类或是一个函数，但我们完全可以用一个内置类型来实例化Bar。这种与内置类型的友好关系是允许的，以便我们能用内置类型来实例化Bar这样的类。

    // 模板类型别名
    typedef Blob<string> StrBlob;
    // 由于模板不是一个类型，我们不能定义一个typedef引用一个模板。即，无法定义一个typedef引用Blob<T>。
    // 但是，新标准允许我们为类模板定义一个类型别名：
    // template<typename T> using twin = pair<T, T>;
    // twin<string> authors; // authors是pair<string, string>的一个实例
    // 在这段代码中，我们将twin定义为成员类型相同的pair的别名。这样，twin的用户只需指定一次类型。

    // 类模板的static成员
    // 与任何其他类相同，类模板可以声明static成员（参见7.6节，第269页）：
    /**
     * template<typename T> class Foo {
     * public:
     *     static std::size_t count() { return ctr; }
     *     // 其他接口成员
     * private:
     *     static std::size_t ctr;
     *     // 其他实现成员
     * };
    */
    // 在这段代码中，Foo是一个类模板，它有一个名为count的public static成员函数和一个名为ctr的private static数据成员。每个Foo的实例都有其自己的static成员实例。
    //   即，对任意给定类型X，都有一个Foo<X>：：ctr和一个Foo<X>：：count成员。所有Foo<X>类型的对象共享相同的ctr对象和count函数。例如，
    //   // 实例化static成员Foo<string>::count和Foo<string>::ctr
    //   Foo<string> fs;
    //   // 所有三个对象共享相同的Foo<int>::count和Foo<int>::ctr
    //   Foo<int> fi, fi2, fi3;
    // 与任何其他static数据成员相同，模板类的每个static数据成员必须有且仅有一个定义。但是，类模板的每个实例都有一个独有的static对象。
    // 因此，与定义模板的成员函数类似，我们将static数据成员也定义为模板：
    //   template<typename T> size_t Foo<T>::ctr = 0; // 定义并初始化ctr
    // 与非模板类的静态成员相同，我们可以通过类类型对象来访问一个类模板的static成员，也可以使用作用域运算符直接访问成员。
    //   Foo<int> fi;                 // 实例化Foo<int>类和static数据成员ctr
    //   auto ct = Foo<int>::count(); // 实例化Foo<int>::count
    //   ct = fi.count();             // 使用Foo<int>::count
    //   ct = Foo::count();           // 错误：使用哪个模板实例的count？

    // 16.1.3 模板参数
    // 类似函数参数的名字，一个模板参数的名字也没有什么内在含义。我们通常将类型参数命名为T，但实际上我们可以使用任何名字：

    // 模板参数与作用域
    // 模板参数遵循普通的作用域规则。一个模板参数名的可用范围是在其声明之后，至模板声明或定义结束之前。与任何其他名字一样，模板参数会隐藏外层作用域中声明的相同名字。

    // 模板声明
    // 与函数参数相同，声明中的模板参数的名字不必与定义中相同：
    // 一个特定文件所需要的所有模板的声明通常一起放置在文件开始位置，出现于任何使用这些模板的代码之前

    // 使用类的类型成员
    // 默认情况下，C++语言假定通过作用域运算符访问的名字不是类型。因此，如果我们希望使用一个模板类型参数的类型成员，就必须显式告诉编译器该名字是一个类型。
    // 我们通过使用关键字typename来实现这一点：
    /**
template<typename T>
typename T::value_type top(const T& c)
{
    if(!c.empty())
        return c.back();
    else
        return typename T::value_type();
}
    */
    // 当我们希望通知编译器一个名字表示类型时，必须使用关键字typename，而不能使用class。

    // 默认模板实参
    // 在新标准中，我们可以为函数和类模板提供默认实参。而更早的C++标准只允许为类模板提供默认实参。
    /**
     * template<typename T, typename F = less<T>>
     * int compare(cosnt T& v1, const T& v2, F f = F())
     * {
     *     if(f(v1, v2)) return -1;
     *     if(f(v2, v1)) return 1;
     *     return 0;
     * }
    */

    // 模板默认实参与类模板
    // 无论何时使用一个类模板，我们都必须在模板名之后接上尖括号。
    /**
     * template<class T = int> class Numbers { // T默认为int
     * public:
     *     Numbers(T v = 0) : val(v) {}
     * private:
     *     T v;
     * };
     * Numbers<> n1; // 空<>表示使用默认类型，等价于Numbers<int> n1;
    */

    // 16.1.4 成员模板
    // 一个类（无论是普通类还是类模板）可以包含本身是模板的成员函数。这种成员被称为成员模板（member template）。成员模板不能是虚函数。

    // 普通（非模板）类的成员模板
    /*
// 函数对象类，对给定指针执行delete
class DebugDelete {
public:
    DebugDelete(std::ostream &os = std::cerr) : os(os) {}
    // 与任何函数模板相同，T的类型由编译器推断
    template<typename T> void operator()(T *p) const { os << "deleting unique_ptr" << endl; delete p; }
private:
    std::ostream &os;
};
double *p = new double;
DebugDelete d; // 可像delete表达式一样使用对象
d(p); // 调用DebugDelete::operator()(double *)，释放p
int *ip = new int;
DebugDelete(ip); // 在一个临时DebugDelete对象上调用operator()(int *)
    */

    // 类模板的成员模板
    // 对于类模板，我们也可以为其定义成员模板。在此情况下，类和成员各自有自己的、独立的模板参数。
    // 例如，我们将为Blob类定义一个构造函数，它接受两个迭代器，表示要拷贝的元素范围。由于我们希望支持不同类型序列的迭代器，因此将构造函数定义为模板：
    /*
template<typename T> class Blob {
    template<typename It> Blob(It b, It e);
    // ...
};
       与类模板的普通函数成员不同，成员模板是函数模板。当我们在类模板外定义一个成员模板时，必须同时为类模板和成员模板提供模板参数列表。
template<typename T>  // 类的参数欸行
template<typename It> // 构造函数的参数类型
Blob<T>::Blob(It b, It e) : data(std::make_shared<std::vector<T>>(b, e)) {}
    */

    // 实例化与成员模板
    // 为了实例化一个类模板的成员模板，我们必须同时提供类和函数模板的实参。与往常一样，我们在哪个对象上调用成员模板，编译器就根据该对象的类型来推断类模板参数的实参。
    // 与普通函数模板相同，编译器通常根据传递给成员模板的函数实参来推断它的模板实参（参见16.1.1节，第579页）：
    /*
int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
vector<long> vi = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<const char*> w = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
// 实例化Blob<int>类及其接受两个int*参数的构造函数
Blob<int> a1(begin(ia), end(ia));
// 实例化Blob<int>类的接受两个vector<long>::iterator的构造函数
Blob<int> a2(vi.begin(), vi.end());
    */
    
    // 16.1.5 控制实例化
    // 当模板被使用时才会进行实例化（参见16.1.1节，第582页）这一特性意味着，相同的实例可能出现在多个对象文件中。当两个或多个独立编译的源文件使用了相同的模板，并提供了相同的模板参数时，
    //   每个文件中就都会有该模板的一个实例。
    // 在大系统中，在多个文件中实例化相同模板的额外开销可能非常严重。在新标准中，我们可以通过显式实例化（explicit instantiation）来避免这种开销。
    //   extern template declaration; // 实例化声明
    //   template dclaration;         // 实例化定义
    // 例如：
    //   extern template class Blob<string>;           // 声明
    //   template int compare(const int&, const int&); // 定义
    // 当编译器遇到extern模板声明时，它不会在本文件中生成实例化代码。将一个实例化声明为extern就表示承诺在程序其他位置有该实例化的一个非extern声明（定义）。
    //   对于一个给定的实例化版本，可能有多个extern声明，但必须只有一个定义。
    // 由于编译器在使用一个模板时自动对其实例化，因此extern声明必须出现在任何使用此实例化版本的代码之前：
    /*
// Application.cc
// 这些模板类型必须在程序其他位置进行实例化
extern template class Blob<string>;
extern template int compare(const int&, const int&);
Blob<stirng> sa1,sa2; // 实例化会出现在其他位置
// Blob<int>及其接受initializer_list的构造函数在本文件中实例化
Blob<int> a1 = { 0,1,2 };
Blob<int> a2(a1); // 拷贝构造函数在本文件中实例化
int i = compare(a1[0],a2[0]); // 实例化出现在其他位置
    */

    // 实例化定义会实例化所有成员
    // 一个类模板的实例化定义会实例化该模板的所有成员，包括内联的成员函数。
    // 当编译器遇到一个实例化定义时，它不了解程序使用哪些成员函数。因此，与处理类模板的普通实例化不同，编译器会实例化该类的所有成员。即使我们不使用某个成员，它也会被实例化。
    // 因此，我们用来显式实例化一个类模板的类型，必须能用于模板的所有成员。
    // 在一个类模板的实例化定义中，所用类型必须能用于模板的所有成员函数。

    // 16.1.6 效率与灵活性
    // 对模板设计者所面对的设计选择，标准库智能指针类型（参见12.1节，第400页）给出了一个很好的展示。
    // shared_ptr和unique_ptr之间的明显不同是它们管理所保存的指针的策略——前者给予我们共享指针所有权的能力；后者则独占指针。这一差异对两个类的功能来说是至关重要的。

    // 在运行时绑定删除器

    // 在编译时绑定删除器



    return 0;
}